home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 May: Tool Chest / Dev.CD May 97 TC.toast / Sample Code / Toolbox / MacCalendar 1.1b1 / MacCalendar.c < prev    next >
Encoding:
Text File  |  1997-03-20  |  21.2 KB  |  747 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        MacCalendar
  3.  
  4.     Contains:    Control strip module for displaying a calendar.
  5.  
  6.     Written by:    Martin Minow
  7.  
  8.     Copyright:    © 1994-1997 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.     You may incorporate this sample code into your applications without
  13.     restriction, though the sample code has been provided "AS IS" and the
  14.     responsibility for its operation is 100% yours.  However, what you are
  15.     not permitted to do is to redistribute the source as "DSC Sample Code"
  16.     after having made changes. If you're going to re-distribute the source,
  17.     we require that you make it clear in the source that the code was
  18.     descended from Apple Sample Code, but that you've made changes.
  19.  
  20.     Based on the Status Bar Sample.c by Steve Christensen.
  21.     
  22.     File Type            sdev
  23.     File Creator        SCAL    -- registered with DTS
  24.     Resource Type        sdev
  25.     Resource ID            0
  26.     Resource Attributes    purgeable
  27.  
  28.     Other options (MetroWerks, Think C 7.0):
  29.         Set Require Prototypes, Check Pointer Types, All other warnings.
  30.         Do not set trigraph recognition.
  31.         Enable Apple extensions.
  32.  
  33.     MetroWerks link/project options:
  34.         Link single segment
  35.         Set Project type "Code Segment", Standard Header.
  36. */
  37.  
  38. /////////////////////////////////////////////////////////////////////////
  39.  
  40. // Pick up resource constants common to both control strip
  41. // module and application.
  42.  
  43. #include "MacCalendarCommon.h"
  44.  
  45. /////////////////////////////////////////////////////////////////////////
  46.  
  47. // Pick up resource constants common to both C and Rez for
  48. // the control strip module.
  49.  
  50. #include "MacCalendar.h"
  51.  
  52. /////////////////////////////////////////////////////////////////////////
  53.  
  54. // Pick up prototype for the core drawing code.
  55.  
  56. #include "DrawCalendar.h"
  57.  
  58. /////////////////////////////////////////////////////////////////////////
  59.  
  60. // Pick up system types.
  61.  
  62. #include <Fonts.h>
  63. #include <Memory.h>
  64. #include <Menus.h>
  65. #include <Quickdraw.h>
  66. #include <Resources.h>
  67. #include <ToolUtils.h>
  68. #include <Types.h>
  69. #include <Windows.h>
  70. #include <Icons.h>
  71. #include <ControlStrip.h>
  72. #include <Gestalt.h>
  73.  
  74. /////////////////////////////////////////////////////////////////////////
  75.  
  76. // Metrowerks uses A4 to reference globals. The A4-setup code was copied from the
  77. // WDEF.c sample included in the Metrowerks DR3 distribution. MPW and Think C
  78. // use PC-relative addressing in a single-segment code module.
  79.  
  80. #ifdef __MWERKS__
  81. #include <A4Stuff.h>    //    also included in <MacHeaders>
  82. #include <SetupA4.h>    //    required to handle callback functions
  83. #endif
  84.  
  85. /////////////////////////////////////////////////////////////////////////
  86.  
  87. // Lots of global constants.
  88.  
  89. /*
  90.  * Define the patterns as C-strings so they can be addressed as constants
  91.  * within the program.
  92.  */
  93. #define kWhitePattern        ((ConstPatternParam) "\000\000\000\000\000\000\000\000")
  94. #define kBlackPattern        ((ConstPatternParam) "\377\377\377\377\377\377\377\377")
  95.  
  96. enum {
  97.     kIconWidth = 16            // Width of the icon on the control strip itself.
  98. };
  99.  
  100. // Name under which our preferences are saved.
  101.  
  102. #define kCalendarPrefName    "\pMacCalendar Preferences"
  103.  
  104. // Indices into the STRN_Info STR# resource.
  105.  
  106. enum {
  107.     kStringHelp = 1,
  108.     kStringFontName,
  109.     kStringFontSize,
  110.     kStringFirstDayOfWeek,
  111.     kStringDayNames
  112. };
  113.  
  114. /*
  115.  * This record defines the information we need to draw the calendar. It is initialized
  116.  * when we are called with the sdevInitModule message, and passed to and from the
  117.  * Status Bar manager.
  118.  */
  119. typedef struct GlobalRecord {
  120.     Handle                iconSuite;                /* Status bar icon                            */
  121.     Handle                textStrings;            /* Balloon help string etc.                    */
  122.     PicHandle            rightArrowPicture;        /* Popup arrow                                */
  123.     SavedSettingsHandle    settings;                /* Preference settings                        */
  124.     UInt32                lastSavedModCount;        /* The modCount setting when we last saved. */
  125. } GlobalRecord, *GlobalPtr, **GlobalHandle;
  126.  
  127. /////////////////////////////////////////////////////////////////////////
  128.  
  129. // Save current status for restarts.
  130.  
  131. static OSErr
  132. CtlStripSavePreferences(
  133.         GlobalPtr                globalPtr
  134.     )
  135. {
  136.         OSErr                    status;
  137.  
  138.         status = SBSavePreferences(kCalendarPrefName, (Handle) globalPtr->settings);
  139.         if (status == noErr) {
  140.             globalPtr->lastSavedModCount = (**globalPtr->settings).modCount;
  141.         }
  142.  
  143.         return(status);
  144. }
  145.  
  146. /////////////////////////////////////////////////////////////////////////
  147.  
  148. // Termination
  149.  
  150. static void
  151. CtlStripCleanUp(
  152.         GlobalHandle            globalHandle
  153.     )
  154. {
  155.         register GlobalPtr        globalPtr;
  156.  
  157.         if (globalHandle != nil) {
  158.             HLock((Handle) globalHandle);
  159.             globalPtr = *globalHandle;
  160.             if (globalPtr->iconSuite != NULL)
  161.                 DisposeIconSuite(globalPtr->iconSuite, TRUE);
  162.             if (globalPtr->textStrings != NULL)
  163.                 DisposeHandle(globalPtr->textStrings);
  164.             if (globalPtr->rightArrowPicture != NULL)
  165.                 DisposeHandle((Handle) globalPtr->rightArrowPicture);
  166.             if (globalPtr->settings != NULL)
  167.                 DisposeHandle((Handle) globalPtr->settings);
  168.             (void) ReplaceGestaltValue(kControlStripCreator, 0);
  169.             DisposeHandle((Handle) globalHandle);
  170.         }
  171. }
  172.  
  173. /////////////////////////////////////////////////////////////////////////
  174.  
  175. // Initialization
  176.  
  177. static long
  178. CtlStripInitialize(void)
  179. {
  180.         register GlobalHandle    globalHandle;
  181.         register GlobalPtr        globalPtr;
  182.         long                    result;
  183.         Str255                    work;
  184.         SavedSettingsHandle        prefsHandle;
  185.         long                    tempLong;
  186.         long                    gestaltResponse;
  187.  
  188.         globalHandle = nil;
  189.         
  190.         // We're using GestaltValue, so we have to make sure we have
  191.         // System 7.5.
  192.         
  193.         result = Gestalt(gestaltSystemVersion, &gestaltResponse);
  194.         if (result == noErr) {
  195.             if (gestaltResponse < 0x0750) {
  196.                 result = unimpErr;
  197.             }
  198.         }
  199.         
  200.         // Register ourselves with Gestalt.  This will trigger
  201.         // an error if we've been installed twice, which will cause the
  202.         // second instance to fail sooner rather than fail later.
  203.         
  204.         if (result == noErr) {
  205.             result = NewGestaltValue(kControlStripCreator, 0);
  206.         }
  207.         if (result == noErr) {
  208.             globalHandle = (GlobalHandle) NewHandleSysClear(sizeof (GlobalRecord));
  209.             result = noErr;
  210.             if (globalHandle == NULL)
  211.                 result = MemError();
  212.             else {
  213.                 HLock((Handle) globalHandle);
  214.                 globalPtr = *globalHandle;
  215.                 /*
  216.                  * Load and detach the icon suite
  217.                  */
  218.                 result = SBGetDetachIconSuite(&(globalPtr->iconSuite), ICON_StatusBar, svAllSmallData);
  219.             }
  220.         }
  221.         if (result == noErr) {
  222.             globalPtr->textStrings = GetResource('STR#', STRN_Info);
  223.             result = ResError();
  224.         }
  225.         if (result == noErr) {
  226.             DetachResource(globalPtr->textStrings);
  227.             globalPtr->rightArrowPicture = GetPicture(PICT_RightArrow);
  228.             if (globalPtr->rightArrowPicture == NULL)
  229.                 result = ResError();
  230.         }
  231.         if (result == noErr) {
  232.             DetachResource((Handle) globalPtr->rightArrowPicture);
  233.  
  234.             /*
  235.              * Get the saved preferences, if any, and configure the drawing
  236.              * environment. Note that the sample status bar doesn't dispose
  237.              * of the prefsHandle but I maintain that this is a bug.  While
  238.              * the control strip documentation doesn't mention whether the
  239.              * handle returned by SBLoadPreferences is a resource or a memory
  240.              * handle, my testing indicates it's a memory handle.
  241.              */
  242.             prefsHandle = nil;
  243.             result = SBLoadPreferences(kCalendarPrefName, (Handle *) &prefsHandle);
  244.             if (result == noErr
  245.              && prefsHandle != nil
  246.              && GetHandleSize((Handle) prefsHandle) == sizeof (SavedSettings)
  247.              && (**prefsHandle).signature == kControlStripCreator
  248.              && (**prefsHandle).prefVersion == kPrefVersion) {
  249.                  /*
  250.                   * Use the saved preference resource
  251.                   */
  252.                 globalPtr->settings = prefsHandle;
  253.                 (**(globalPtr->settings)).modCount = 0;
  254.                 globalPtr->lastSavedModCount = 0;
  255.             }
  256.             else {
  257.                 /*
  258.                  * Hmm, we don't have any preferences. Build a new preference resource.
  259.                  */
  260.                 if (prefsHandle != nil) {
  261.                     DisposeHandle( (Handle) prefsHandle);
  262.                 }
  263.                 prefsHandle = (SavedSettingsHandle) NewHandleSysClear(sizeof(SavedSettings));
  264.                 globalPtr->settings = prefsHandle;
  265.                 result = MemError();
  266.                 if (result == noErr) {
  267.                     (**(globalPtr->settings)).signature = kControlStripCreator;
  268.                     (**(globalPtr->settings)).prefVersion = kPrefVersion;
  269.                     (**(globalPtr->settings)).modCount = 0;
  270.                     globalPtr->lastSavedModCount = 1;
  271.                     
  272.                     SBGetDetachedIndString(work, globalPtr->textStrings, kStringDayNames);
  273.                     pstrcpy((**(globalPtr->settings)).dayNameString, work);
  274.                     
  275.                     SBGetDetachedIndString(work, globalPtr->textStrings, kStringFontName);
  276.                     pstrcpy((**(globalPtr->settings)).fontName, work);
  277.                     
  278.                     SBGetDetachedIndString(work, globalPtr->textStrings, kStringFontSize);
  279.                     StringToNum(work, &tempLong);
  280.                     (**(globalPtr->settings)).fontSize = tempLong;
  281.                     
  282.                     SBGetDetachedIndString(work, globalPtr->textStrings, kStringFirstDayOfWeek);
  283.                     StringToNum(work, &tempLong);
  284.                     (**(globalPtr->settings)).firstDayOfWeek = tempLong;
  285.  
  286.                 }
  287.             }
  288.         }
  289.  
  290.         // Now that we successfully started up, publish our globals using
  291.         // Gestalt.
  292.         
  293.         if (result == noErr) {
  294.             result = ReplaceGestaltValue(kControlStripCreator, (long) globalPtr->settings);
  295.         }
  296.         /*
  297.          * We've finished all initialization. If there is an error, exit through
  298.          * CtlStripCleanUp to dispose of handles and other junk. If initialization
  299.          * are successful, unlock the handle and return the handle cast to a long.
  300.          */
  301.         if (result != noErr) {
  302.             CtlStripCleanUp(globalHandle);
  303.         }
  304.         else {
  305.             HUnlock((Handle) globalHandle);
  306.             result = (long) globalHandle;
  307.         }
  308.         return (result);
  309. }
  310.  
  311. /////////////////////////////////////////////////////////////////////////
  312.  
  313. // Draw the icon in the status bar.
  314.  
  315. static long
  316. CtlStripDrawStatusIcon(
  317.         GlobalPtr                globalPtr,
  318.         const Rect                *statusRect
  319.     )
  320. {
  321.         Rect                    viewRect;
  322.         short                    arrowHeight;
  323.  
  324.         viewRect = *statusRect;
  325.         viewRect.right = viewRect.left + kIconWidth;
  326.         (void) PlotIconSuite(&viewRect, atNone, ttNone, globalPtr->iconSuite);
  327.         /*
  328.          * Draw an right-arrow to show that we have a popup menu. Well, we don't
  329.          * actually have a popup menu, but we do pop up a calendar when clicked on.
  330.          */
  331.         arrowHeight = height(PicFrame(rightArrowPicture));
  332.         viewRect.left = viewRect.right;
  333.         viewRect.right += width(PicFrame(rightArrowPicture));
  334.         viewRect.top += ((height(viewRect) - arrowHeight) >> 1);
  335.         viewRect.bottom = viewRect.top + arrowHeight;
  336.         DrawPicture(globalPtr->rightArrowPicture, &viewRect);
  337.         return (0);
  338. }
  339.  
  340. /////////////////////////////////////////////////////////////////////////
  341.  
  342. // Position the calendar with respect to the status bar. If there is enough room
  343. // above, put it above, else put it below. Left and right operate similarly.
  344.  
  345. enum {
  346.     kCtlStripFrame = 4            /* The frame above/below the icon itself */
  347. };
  348.  
  349. static void
  350. GetDisplayRect(
  351.         const Rect                *statusRect,
  352.         Point                    displaySize,
  353.         Rect                    *windowRect
  354.     )
  355. {
  356.         Rect                    sBarRect;
  357.  
  358.         sBarRect = *statusRect;
  359.         LocalToGlobal((Point *) &sBarRect.top);
  360.         LocalToGlobal((Point *) &sBarRect.bottom);
  361.         if (sBarRect.top - kCtlStripFrame - displaySize.v - (GetMBarHeight() + 2) > 0) {
  362.             /*
  363.              * The calendar is displayed above the status bar.
  364.              */
  365.             windowRect->bottom = sBarRect.top - kCtlStripFrame - 1;
  366.             windowRect->top = windowRect->bottom - displaySize.v;
  367.         }
  368.         else {
  369.             /*
  370.              * The calendar is displayed below the status bar.
  371.              */
  372.             windowRect->top = sBarRect.bottom + kCtlStripFrame + 1;
  373.             windowRect->bottom = windowRect->top + displaySize.v;
  374.         }
  375.         if (sBarRect.right - displaySize.h > 0) {
  376.             /*
  377.              * The calendar is displayed to the left of the calendar icon.
  378.              */
  379.             windowRect->right = sBarRect.right;
  380.             windowRect->left = windowRect->right - displaySize.h;
  381.         }
  382.         else {
  383.             /*
  384.              * The calendar is displayed to the right of the calendar icon.
  385.              */
  386.             windowRect->left = sBarRect.left;
  387.             windowRect->right = windowRect->left + displaySize.h;
  388.         }
  389. }
  390.  
  391.  
  392. /////////////////////////////////////////////////////////////////////////
  393.  
  394. // Build the triangular "next month" and "previous month" buttons.
  395.  
  396. enum {
  397.     kButtonSeparation = 4                    /* 1.0d3, was 2                    */
  398. };
  399.  
  400. static void
  401. MakeTriangularButtons(
  402.         const Rect                *monthRect,
  403.         PolyHandle                *leftButton,
  404.         PolyHandle                *rightButton,
  405.         Rect                    *leftButtonRect,
  406.         Rect                    *rightButtonRect
  407.     )
  408. {
  409.         FontInfo                fontInfo;
  410.         short                    buttonSize;
  411.         short                    halfSize;
  412.         Rect                    bothButtonRect;
  413.  
  414.         GetFontInfo(&fontInfo);
  415.         buttonSize = (fontInfo.ascent & ~1);    /* Round down to even value        */
  416.         halfSize = buttonSize / 2;
  417.         bothButtonRect = *monthRect;
  418.         bothButtonRect.bottom -= (1 + fontInfo.leading);
  419.         bothButtonRect.top = bothButtonRect.bottom - buttonSize;
  420.         bothButtonRect.left =
  421.             (width(*monthRect) >> 1) - buttonSize - kButtonSeparation;
  422.         bothButtonRect.right =
  423.             (width(*monthRect) >> 1) + buttonSize + kButtonSeparation;
  424.         *leftButtonRect = bothButtonRect;
  425. /* 1.0d4 +    */
  426.         leftButtonRect->right = leftButtonRect->left + halfSize;
  427.         *rightButtonRect = bothButtonRect;
  428.         rightButtonRect->left = rightButtonRect->right - halfSize;
  429.         *leftButton = OpenPoly();
  430.             MoveTo(halfSize, 0);
  431.             LineTo(halfSize, buttonSize);
  432.             LineTo(0, halfSize);
  433.             LineTo(halfSize, 0);
  434.         ClosePoly();
  435.         OffsetPoly(*leftButton, leftButtonRect->left, leftButtonRect->top);
  436.         *rightButton = OpenPoly();
  437.             MoveTo(0, 0);
  438.             LineTo(halfSize, halfSize);
  439.             LineTo(0, buttonSize);
  440.             LineTo(0, 0);
  441.         ClosePoly();
  442.         OffsetPoly(*rightButton, rightButtonRect->left, rightButtonRect->top);
  443. /* 1.0d4 -    */
  444. }
  445.  
  446. /////////////////////////////////////////////////////////////////////////
  447.  
  448. // Now that we've done setup, do the actual mouse tracking. If the mouse hits the
  449. // right button, advance the month and draw it. If it hits the left button, draw
  450. // the previous month.
  451.  
  452. static void
  453. DrawCalendarAndTrackMouse(
  454.         GlobalPtr                globalPtr,
  455.         WindowPtr                windowPtr,
  456.         const Rect                *monthRect
  457.     )
  458. {
  459.         Rect                    leftButtonRect;
  460.         Rect                    rightButtonRect;
  461.         PolyHandle                leftButton;
  462.         PolyHandle                rightButton;
  463.         unsigned long            nowSeconds;
  464.         DateTimeRec                now;
  465.         Point                    mousePt;
  466.         short                    thisYear;
  467.         short                    thisMonth;
  468.         typedef enum {
  469.             inNoButton = 0,
  470.             inLeftButton = 1,
  471.             inRightButton = 2
  472.         } WhichButton;
  473.         WhichButton                inButton;
  474.         WhichButton                wasInButton;
  475.         unsigned long            nextMonthTick;
  476.         Boolean                    redrawButtons;
  477.  
  478.         FrameRect(monthRect);
  479.         MakeTriangularButtons(
  480.             monthRect,
  481.             &leftButton,
  482.             &rightButton,
  483.             &leftButtonRect,
  484.             &rightButtonRect
  485.         );
  486.         GetDateTime(&nowSeconds);
  487.         SecondsToDate(nowSeconds, &now);
  488.         inButton = wasInButton = inNoButton;
  489.         thisYear = thisMonth = 0;
  490.         InitCursor();
  491.         while (WaitMouseUp()) {
  492.             if (thisYear != now.year || thisMonth != now.month) {
  493.                 /*
  494.                  * Draw the new month. Also make sure the buttons are drawn.
  495.                  */
  496.                 redrawButtons = TRUE;
  497.                 EraseRect(&windowPtr->portRect);
  498.                 FrameRect(monthRect);
  499.                 switch (wasInButton) {
  500.                 case inLeftButton:
  501.                     FillPoly(leftButton, kWhitePattern);
  502.                     break;
  503.                 case inRightButton:
  504.                     FillPoly(rightButton, kWhitePattern);
  505.                     break;
  506.                 }
  507.                 FramePoly(leftButton);
  508.                 FramePoly(rightButton);
  509.                 DrawCalendar(
  510.                     globalPtr->settings,
  511.                     now.year,
  512.                     now.month,
  513.                     &windowPtr->portRect
  514.                 );
  515.                 thisYear = now.year;
  516.                 thisMonth = now.month;
  517.             }                                            /* If drawing new month        */
  518.             /*
  519.              * Get the mouse and track it while it is in one of our buttons
  520.              */
  521.             GetMouse(&mousePt);
  522.             if (PtInRect(mousePt, &leftButtonRect))
  523.                 inButton = inLeftButton;
  524.             else if (PtInRect(mousePt, &rightButtonRect))
  525.                 inButton = inRightButton;
  526.             else {
  527.                 inButton = inNoButton;
  528.             }
  529.             if (redrawButtons || inButton != wasInButton) {
  530.                 switch (wasInButton) {
  531.                 case inLeftButton:    FillPoly(leftButton, kWhitePattern);    break;
  532.                 case inRightButton:    FillPoly(rightButton, kWhitePattern);    break;
  533.                 }
  534.                 switch (inButton) {
  535.                 case inLeftButton:    FillPoly(leftButton, kBlackPattern);    break;
  536.                 case inRightButton:    FillPoly(rightButton, kBlackPattern);    break;
  537.                 }
  538.                 FramePoly(leftButton);
  539.                 FramePoly(rightButton);
  540.                 if (inButton != wasInButton && inButton != inNoButton)
  541.                     nextMonthTick = 0;                    /* Force new month drawing    */
  542.                 redrawButtons = FALSE;
  543.                 wasInButton = inButton;
  544.             }                                            /* If button click change    */
  545.             if (inButton != inNoButton && TickCount() > nextMonthTick) {
  546.                 /*
  547.                  * The user has clicked in a button, or has held the mouse
  548.                  * down in a button for one second. Draw the appropriate month.
  549.                  */
  550.                 nextMonthTick = TickCount() + 60;
  551.                 switch (inButton) {
  552.                 case inLeftButton:
  553.                     if (--now.month <= 0) {                /* Previous month or year    */
  554.                         now.month = 12;
  555.                         --now.year;
  556.                     }
  557.                     break;
  558.                 case inRightButton:
  559.                     if (++now.month > 12) {                /* Next month or year        */
  560.                         now.month = 1;
  561.                         ++now.year;
  562.                     }
  563.                     break;
  564.                 }                                        /* Which button was clicked    */
  565.             }                                            /* Moving to a new month    */
  566.         }                                                /* Loop while mouse down    */
  567.         KillPoly(leftButton);
  568.         KillPoly(rightButton);
  569. }
  570.  
  571. /////////////////////////////////////////////////////////////////////////
  572.  
  573. // Click in the status bar icon
  574.  
  575. static long
  576. CtlStripMouseClick(
  577.         GlobalPtr                globalPtr,
  578.         const Rect                *statusRect
  579.     )
  580. {
  581.         Rect                    windowRect;
  582.         WindowPtr                windowPtr;
  583.         GrafPtr                    savePort;
  584.         Point                    displaySize;
  585.         Rect                    monthRect;
  586.  
  587.         displaySize = GetCalendarDisplaySize(globalPtr->settings);
  588.         displaySize.h += 2;
  589.         displaySize.v += 2;
  590.         GetDisplayRect(statusRect, displaySize, &windowRect);
  591.         windowPtr = NewWindow(
  592.                     NULL,
  593.                     &windowRect,
  594.                     "\p",
  595.                     TRUE,
  596.                     plainDBox,
  597.                     (WindowPtr) -1L,
  598.                     FALSE,                                /* No go-away box            */
  599.                     0                                    /* No refCon                */
  600.                 );
  601.         if (windowPtr != NULL) {
  602.             GetPort(&savePort);
  603.             SetPort(windowPtr);
  604.             GetCalendarMonthRect(
  605.                 globalPtr->settings,
  606.                 &windowPtr->portRect,
  607.                 &monthRect
  608.             );
  609.             if (StillDown()) {
  610.                 /*
  611.                  * Design the two triangular buttons that will be displayed on the
  612.                  * bottom line of the calendar and create the polygons. Then draw
  613.                  * the calendar and track the mouse while it's held down.
  614.                  */
  615.                 DrawCalendarAndTrackMouse(
  616.                     globalPtr,
  617.                     windowPtr,
  618.                     &monthRect
  619.                 );
  620.             }
  621.             SetPort(savePort);
  622.             DisposeWindow(windowPtr);
  623.         }
  624.         return (0);
  625. }
  626.  
  627. /////////////////////////////////////////////////////////////////////////
  628.  
  629. // Periodically we check the globals that we publish via Gestalt.  If they
  630. // have been modified, we return sdevNeedToSave so that the Control Strip
  631. // will call our CtlStripSavePreferences routine when an appropriate time
  632. // to save comes around (ie the hard disk spins up).
  633.  
  634. static long
  635. CtlStripPeriodicTickle(
  636.         GlobalPtr                globalPtr,
  637.         const Rect                *statusRect
  638.     )
  639. {
  640.         #pragma unused(statusRect)
  641.         OSErr result;
  642.         
  643.         result = 0;
  644.         if (globalPtr->lastSavedModCount != (**globalPtr->settings).modCount) {
  645.             result = (1 << sdevNeedToSave);
  646.         }
  647.         return (result);
  648. }
  649.  
  650. /////////////////////////////////////////////////////////////////////////
  651.  
  652. // This "main" program is called by the Control Strip manager.  Note that,
  653. // unlike the rest of this program, we must define a prototype for main
  654. // because we can't just declare it "static".
  655.  
  656. pascal long                    main(
  657.         unsigned long            message,
  658.         GlobalHandle            globalHandle,
  659.         const Rect                *statusRect,
  660.         GrafPtr                    statusPort
  661.     );
  662.  
  663. pascal long
  664. main(
  665.         unsigned long            message,
  666.         GlobalHandle            globalHandle,
  667.         const Rect                *statusRect,
  668.         GrafPtr                    statusPort
  669.     )
  670. {
  671.         #pragma unused(statusPort)
  672.         short                    savedState;
  673.         register GlobalPtr        globalPtr;
  674.         long                    result;
  675.         Str255                    helpString;
  676. #ifdef __MWERKS__
  677.         long                    oldA4 = SetCurrentA4();
  678. #endif
  679.  
  680.         if (globalHandle != nil) {
  681.             /*
  682.              * We have already allocated the global record. Save its lock state, lock
  683.              * the handle, and get the global pointer (so we can write (*globalPtr).something)
  684.              */
  685.             savedState = HGetState((Handle) globalHandle);
  686.             HLock((Handle) globalHandle);
  687.             globalPtr = *globalHandle;
  688.         }
  689.         result = 0;                                    /* Unknown message result        */
  690.         switch (message) {
  691.         case sdevInitModule:                        /* Initialize the module        */
  692.             /*
  693.              * Initialization always sets globalHandle to NULL to avoid the HSetState
  694.              * at the exit routine. If CtlStripInitialize succeeds, it sets the result
  695.              * to the global parameter.
  696.              */
  697.             globalHandle = NULL;
  698.             result = CtlStripInitialize();            /* Do the initialization and    */
  699.             break;                                    /* Return global or error code    */
  700.         case sdevCloseModule:                        /* Clean up before closing        */
  701.             CtlStripCleanUp(globalHandle);
  702.             globalHandle = NULL;
  703.             break;
  704.         case sdevFeatures:                            /* Return feature bits            */
  705.             result = (  (1<<sdevWantMouseClicks)    /* We handle mouse down            */
  706.                       | (1<<sdevDontAutoTrack)        /* We track the mouse, too        */
  707.                       | (1<<sdevHasCustomHelp)        /* Custom help string            */
  708.                     );
  709.             break;
  710.         case sdevGetDisplayWidth:                    /* Return display width            */
  711.             result = kIconWidth    + width(PicFrame(rightArrowPicture));
  712.             break;
  713.         case sdevPeriodicTickle:                    /* Nothing else is happening    */
  714.             result = CtlStripPeriodicTickle(globalPtr, statusRect);
  715.             break;
  716.         case sdevDrawStatus:                        /* Draw the status bar info        */
  717.             result = CtlStripDrawStatusIcon(globalPtr, statusRect);
  718.             break;
  719.         case sdevMouseClick:                        /* Status bar click                */
  720.             result = CtlStripMouseClick(globalPtr, statusRect);
  721.             break;
  722.         case sdevSaveSettings:                        /* Save changed settings        */
  723.             result = CtlStripSavePreferences(globalPtr);
  724.             break;
  725.         case sdevShowBalloonHelp:                    /* Display custom balloon help    */
  726.             /*
  727.              * We don't really have a custom help string, but this shows how to do it.
  728.              */
  729.             SBGetDetachedIndString(helpString, globalPtr->textStrings, kStringHelp);
  730.             SBShowHelpString(statusRect, helpString);
  731.             break;
  732.         default:
  733.             // Ignore unknown messages.
  734.             break;
  735.         }
  736.         
  737.         if (globalHandle != NULL)                    /* If we have globals allocated    */
  738.             HSetState((Handle) globalHandle, savedState);    /* Restore lock state    */    
  739.  
  740. #ifdef __MWERKS__
  741.         SetA4(oldA4);
  742. #endif
  743.         return (result);
  744. }
  745.  
  746.  
  747.